package com.strato.annotation.osgi.v1_0_0.service.util.inline;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

import org.osgi.framework.BundleContext;

import com.strato.annotation.osgi.v1_0_0.activator.Activator;
import com.strato.base.aop.v1_0_0.Invocation;
import com.strato.base.aop.v1_0_0.InvocationInterceptor;
import com.strato.base.aop.v1_0_0.advice.Advice;
import com.strato.base.aop.v1_0_0.advice.AdvicedInvocation;
import com.strato.base.osgi.v1_0_0.BundleUtil;
import com.strato.base.osgi.v1_0_0.Ordered;
import com.strato.ncache.v2_6_5.DataLoader;
import com.strato.ncache.v2_6_5.NCache;

public class OsgiMethodInterceptor implements InvocationInterceptor {

	private static OsgiMethodInterceptor instance=null;
	
	public synchronized static OsgiMethodInterceptor getInstance(){
		if(instance==null){
			instance=new OsgiMethodInterceptor();
		}
		return instance;
	}

	private NCache<List<Advice>> adviceCache=NCache.newInstance();
	private DataLoader<List<Advice>> loader=new DataLoader<List<Advice>>() {
		@Override
		public List<Advice> load() throws Exception {
			BundleContext context = Activator.getContext();
			List<Advice> advices = BundleUtil.getServices(context, Advice.class, null);
			Collections.sort(advices,Ordered.DECEND);
			return advices;
		}
	};
	
	private List<Advice> getAdvices() {
		return adviceCache.get(NCache.DEFAULT_KEY, loader);
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		List<Advice> advices=getAdvices();
		if(advices==null){
			return invocation.proceed();
		}
		Object o = invocation.getObject();
		Method method = invocation.getMethod();
		Object[] args = invocation.getArgs();
		for(Advice advice:advices){
			if(advice.supports(o, method, args)){
				invocation=new AdvicedInvocation(invocation,advice);
			}
		}
		return invocation.proceed();
	}

}
